import datetime
import uuid
import os
from django.http import JsonResponse, HttpResponseForbidden
from django.shortcuts import render
from fort.models import *
from assets.models import ServerAssets, IPGroup
from django.contrib.auth.models import Group
from users.models import UserProfile
from task.utils.gen_resource import GenResource
from task.utils.ansible_api_v2 import ANSRunner
from django.contrib.auth.decorators import permission_required
from django.conf import settings
from utils.sftp import SFTP
from utils.decorators import admin_auth, permission_alternate_required
from assets.models import Assets
from django.db.models import Q
from utils.crypt_pwd import CryptPwd
import json
from projs.views import download_response
import xlsxwriter
import xlrd
import re
import importlib


@permission_required('assets.add_assets', raise_exception=True)
def fast_add_connect(request, pk):
    fort_user_status = FortServerUser.fort_user_status_
    users = UserProfile.objects.all()
    groups = Group.objects.all()
    server_obj = FortServer.objects.filter(server__asset_management_ip__serverassets=pk)
    connect_flag = {}
    server_ip = ServerAssets.objects.filter(id=pk)[0].assets.asset_management_ip
    fort_code_ = FortServerUser.fort_code_
    for obj in server_obj:
        connect_flag[obj.server_protocol] = obj
    return render(request, 'fort/fast_add_connect.html', locals())


@permission_alternate_required(['fort.change_fortserveruser', 'fort.view_fort_password'], raise_exception=True)
def search_fort_password(request, pk):
    if request.method == 'GET':
        fortserveruser = FortServerUser.objects.get(id=pk)
        results = {'fort_password': CryptPwd().decrypt_pwd(fortserveruser.fort_password)}
        return JsonResponse({'code': 200, 'results': results})


@permission_required('fort.change_fortserveruser', raise_exception=True)
def batch_change_fort_belong_user(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        type = data.get('type', 'update')
        fort_belong_user = data.get('fort_belong_user', False)
        fort_id = data.get('fort_id', False)
        count = 0
        if fort_belong_user and fort_id:
            fortserveruser = FortServerUser.objects.filter(fort_server_id=fort_id)
            if type == 'add':
                for user in fortserveruser:
                    user.fort_belong_user.remove(*fort_belong_user)
                    user.fort_belong_user.add(*fort_belong_user)
                    user.save()
                    count += 1
            elif type == 'update':
                for user in fortserveruser:
                    user.fort_belong_user.clear()
                    user.fort_belong_user.add(*fort_belong_user)
                    user.save()
                    count += 1
                count += 1
            elif type == 'delete':
                for user in fortserveruser:
                    user.fort_belong_user.remove(*fort_belong_user)
                    user.save()
                    count += 1
            return JsonResponse({'code': 200 ,'count': count})
        return JsonResponse({'code': 500 })


@permission_required('fort.change_fortserveruser', raise_exception=True)
def batch_change_fort_belong_group(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        type = data.get('type', 'update')
        fort_belong_group = data.get('fort_belong_group', False)
        fort_id = data.get('fort_id', False)
        count = 0
        if fort_belong_group and fort_id:
            fortserveruser = FortServerUser.objects.filter(fort_server_id=fort_id)
            if type == 'add':
                for user in fortserveruser:
                    user.fort_belong_group.remove(*fort_belong_group)
                    user.fort_belong_group.add(*fort_belong_group)
                    user.save()
                    count += 1
            elif type == 'update':
                for user in fortserveruser:
                    user.fort_belong_group.clear()
                    user.fort_belong_group.add(*fort_belong_group)
                    user.save()
                    count += 1
                count += 1
            elif type == 'delete':
                for user in fortserveruser:
                    user.fort_belong_group.remove(*fort_belong_group)
                    user.save()
                    count += 1
            return JsonResponse({'code': 200 ,'count': count})
        return JsonResponse({'code': 500 })


def find_username(request):
    if request.method == 'GET':
        serverassets = request.GET.get('serverassets',False)
        fort_server_id = request.GET.get('fort_server_id', False)
        is_ssh = request.GET.get('is_ssh', '0')
        if serverassets:
            username = FortServerUser.objects.filter(~Q(fort_server__server_protocol='vnc'),fort_server__server__asset_management_ip__serverassets=serverassets)
            if is_ssh == '1':
                username = username.filter(fort_server__server_protocol='ssh')
            results = [{'id': i.id, 'text': i.fort_username, 'protocol': i.fort_server.server_protocol} for i in username]
        if fort_server_id:
            username = FortServerUser.objects.filter(~Q(fort_server__server_protocol='vnc'),fort_server_id=fort_server_id)
            results = [{'id': i.id, 'text': i.fort_username} for i in username]
        return JsonResponse({'code': 200, 'results': results})

def find_vnc_connect(request):
    if request.method == 'GET':
        fort_server_id = request.GET.get('fort_server_id', False)
        if fort_server_id:
            vnc_connect = FortServerUser.objects.filter(fort_server__server_protocol='vnc',fort_server_id=fort_server_id)
            results = [{'id': i.id, 'text': '端口：'+i.fort_vnc_port + ' - 对象：' + i.fort_username if i.fort_username else '端口：'+i.fort_vnc_port} for i in vnc_connect]
        return JsonResponse({'code': 200, 'results': results})

def find_custom_hosts(request):
    from django.core.paginator import Paginator
    if request.method == 'GET':
        search = request.GET.get('search')
        page = request.GET.get('page')
        offset = request.GET.get('offset')
        assets = Assets.objects.filter(
            Q(asset_management_ip__IP__contains=search) | Q(asset_nu__icontains=search)).values_list('asset_management_ip__id',
                                                                                                     'asset_management_ip__IP').order_by('asset_management_ip__IP').distinct()
        group = Paginator(assets, offset).page(page)
        results = [{'id': i[0], 'text': i[1]} for i in group.object_list]
        return JsonResponse(
            {'code': 200, 'results': results, 'page': group.paginator.num_pages, 'total_count': group.paginator.count})


def find_ssh_connect(request):
    from django.core.paginator import Paginator
    if request.method == 'GET':
        search = request.GET.get('search')
        page = request.GET.get('page')
        offset = request.GET.get('offset')
        assets = Assets.objects.filter(Q(asset_management_ip__IP__contains=search) | Q(asset_nu__icontains=search), asset_management_ip__fortserver__server_protocol='ssh', asset_management_ip__fortserver__fortserveruser__id__isnull=False)
        results = assets.values_list('asset_management_ip__fortserver__fortserveruser__id', 'asset_management_ip__IP',  'asset_management_ip__fortserver__fortserveruser__fort_username').order_by('asset_management_ip__IP').distinct()
        group = Paginator(results, offset).page(page)
        results = [{'id': i[0], 'text': 'IP地址 '+i[1]+' 用户名 '+i[2]} for i in group.object_list]
        return JsonResponse(
            {'code': 200, 'results': results, 'page': group.paginator.num_pages, 'total_count': group.paginator.count})
    if request.method == 'POST':
        data = json.loads(request.body)
        ids = data.get('ids', False)
        if ids:
            fortserveruser = FortServerUser.objects.filter(id__in=ids).values_list('id', 'fort_server__server__IP', 'fort_username')
            results = [{'id': i[0], 'text': 'IP地址 '+i[1]+' 用户名 '+i[2]} for i in fortserveruser]
        else:
            results = {}
        return JsonResponse({'code': 200, 'results': results})


@permission_required('fort.view_fortserver', raise_exception=True)
def fort_server(request):
    # hosts_ = ServerAssets.objects.select_related('assets')
    # hosts = set(map(lambda x: x.assets.asset_management_ip, hosts_ ))
    # sorted(hosts,key= lambda x:x.id)
    server_protocols = FortServer.server_protocols
    server_status = FortServer.server_status_
    fort_user_status = FortServerUser.fort_user_status_
    fort_code_ = FortServerUser.fort_code_
    users = UserProfile.objects.all()
    groups = Group.objects.all()
    asset_environment_ = Assets.asset_environment_
    remote_ip = request.META['REMOTE_ADDR']
    return render(request, 'fort/fort_server.html', locals())


@permission_required('fort.ssh_fortserver', raise_exception=True)
def ssh_list(request):
    server_protocols = FortServer.server_protocols
    fort_users = gen_fort_users(request.user)
    asset_environment_ = Assets.asset_environment_
    return render(request, 'fort/ssh_list.html', locals())


@permission_required('fort.ssh_fortserver', raise_exception=True)
def terminal(request, fort_user_id):
    fort_user_obj = FortServerUser.objects.get(id=fort_user_id)
    fort_username = fort_user_obj.fort_username
    server_ip = fort_user_obj.fort_server.server.IP
    port = fort_user_obj.fort_server.port

    if request.method == 'GET':
        download_file = request.GET.get('download_file')
        if download_file:

            download_file_path = os.path.join(settings.MEDIA_ROOT, 'fort_files', request.user.username, 'download',
                                              server_ip)
            sftp = SFTP(server_ip, port, fort_username,
                        CryptPwd().decrypt_pwd(fort_user_obj.fort_password))

            response = sftp.download_file(download_file, download_file_path)
            return response
        else:
            fort_users = gen_fort_users(request.user)

            if fort_user_obj in fort_users:
                remote_ip = request.META.get('REMOTE_ADDR')
                server_protocol = fort_user_obj.fort_server.server_protocol
                group_name = str(uuid.uuid4())
                if server_protocol == 'ssh':
                    return render(request, 'fort/terminal.html', locals())
                elif server_protocol == 'rdp':
                    return render(request, 'fort/guacamole.html', locals())
                elif settings.GUACD_TELNET_PROTOCOL and importlib.util.find_spec('guacamole'):
                    return render(request, 'fort/telnet_guacamole.html', locals())
                return render(request, 'fort/terminal.html', locals())
            else:
                return HttpResponseForbidden('<h1>你的账户不存在连接的所属用户组中！您无权连接此主机！</h1>')
    elif request.method == 'POST':
        try:
            upload_file = request.FILES.get('upload_file')
            upload_file_path = os.path.join(settings.MEDIA_ROOT, 'fort_files', request.user.username, 'upload',
                                            server_ip)
            sftp = SFTP(server_ip, port, fort_username,
                        CryptPwd().decrypt_pwd(fort_user_obj.fort_password))

            sftp.upload_file(upload_file, upload_file_path)

            return JsonResponse({'code': 200, 'msg': '上传成功！文件默认放在/home/{}路径下'.format(fort_username)})
        except Exception as e:
            return JsonResponse({'code': 500, 'msg': '上传失败！{}'.format(e)})


def gen_fort_users(user):
    """
    生成当前用户下的所有可用的用户列表
    :param user: 当前用户，request.user
    """
    groups = user.groups.all()

    fort_server_users = FortServerUser.objects.prefetch_related('fort_belong_group').all()

    fort_users = {i for i in FortServerUser.objects.prefetch_related('fort_belong_user').filter(fort_belong_user=user)}

    for fort_server_user in fort_server_users:
        if not set(fort_server_user.fort_belong_group.all()).isdisjoint(set(groups)):  # 判断fort_user的组与当前用户的组是否有交集
            fort_users.add(fort_server_user)

    fort_users = [user for user in fort_users if user.fort_server.server_status == 1 and user.fort_user_status == 1]

    users = fort_server_users if user.is_superuser else fort_users
    return users


@permission_required('fort.view_fortrecord', raise_exception=True)
def login_fort_record(request):
    if request.method == 'GET':
        results = FortRecord.objects.select_related('login_user').all()
        return render(request, 'fort/login_fort_log.html', locals())
    elif request.method == 'POST':
        start_time = request.POST.get('startTime')
        end_time = request.POST.get('endTime')
        new_end_time = datetime.datetime.strptime(end_time, '%Y-%m-%d') + datetime.timedelta(1)
        end_time = new_end_time.strftime('%Y-%m-%d')
        try:
            records = []
            search_records = FortRecord.objects.select_related('login_user').filter(start_time__gt=start_time,
                                                                                    start_time__lt=end_time)
            for search_record in search_records:
                record = {
                    'id': search_record.id,
                    'login_user': search_record.login_user.username,
                    'fort': search_record.fort,
                    'record_mode': search_record.get_record_mode_display(),
                    'remote_ip': search_record.remote_ip,
                    'start_time': search_record.start_time,
                    'login_status_time': search_record.login_status_time,
                    'record_cmds': search_record.record_cmds
                }
                records.append(record)
            return JsonResponse({'code': 200, 'records': records})
        except Exception as e:
            return JsonResponse({'code': 500, 'error': '查询失败：{}'.format(e)})


@permission_required(['fort.view_fortrecord','fort.ssh_fortserver'], raise_exception=True)
def record_play(request, pk):
    record = FortRecord.objects.select_related('login_user').get(id=pk)
    if record.record_mode == 'ssh':
        return render(request, 'fort/ssh_record_play.html', locals())
    else:
        return render(request, 'fort/guacamole_record_play.html', locals())

@permission_required(['fort.add_fortserver', 'fort.add_fortserveruser'], raise_exception=True)
def download_fort_template(request):
    if request.method == 'GET':
        tmp_dir = os.path.join(os.path.join(settings.BASE_DIR, 'media'), 'tmp')
        if not os.path.exists(tmp_dir):
            os.mkdir(tmp_dir)
        filename = os.path.join(tmp_dir, str(uuid.uuid1()).replace('-', '') + '.xlsx')
        workbook = xlsxwriter.Workbook(filename)
        worksheet = workbook.add_worksheet(u'服务器信息导入模板')
        title = workbook.add_format(
            {'bold': True, 'align': 'center', 'valign': 'vcenter', 'bg_color': '#007ee5', 'border': 1, })
        worksheet.set_column(0, 0, 15)
        worksheet.set_column(1, 1, 10)
        worksheet.set_column(2, 2, 15)
        worksheet.set_column(3, 3, 15)
        worksheet.set_column(4, 4, 15)
        worksheet.set_column(5, 5, 10)
        worksheet.set_column(6, 6, 15)
        worksheet.set_column(7, 7, 20)
        row = [u'主机IP地址', u'协议', u'连接端口', u'用户名称', u'用户密码', u'字符编码', u'VNC端口', u'备注']
        for r in range(len(row)):
            worksheet.write(0, r, row[r], title)
        worksheet.freeze_panes(1, 0)
        worksheet.autofilter('A1:H1')
        worksheet.data_validation(1, 1, 10000, 1, {'validate': 'list', 'source': list(dict(FortServer.server_protocols).values())})
        worksheet.data_validation(1, 5, 10000, 5,
                                  {'validate': 'list', 'source': list(dict(FortServerUser.fort_code_).values())})
        workbook.close()
        response = download_response(filename)
        return response

@permission_required(['fort.add_fortserver', 'fort.add_fortserveruser'], raise_exception=True)
def import_fort(request):
    if request.method == 'POST':
        file = request.FILES.get('file')
        filename = os.path.join(settings.BASE_DIR, 'upload', file.name)
        if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
        with open(filename, 'wb') as f:
            for chunk in file.chunks():
                f.write(chunk)

        try:
            # 只支持导入主机、协议、账号同时唯一的信息！
            xl_file = xlrd.open_workbook(filename)
            sheet_names = xl_file.sheet_names()
            data_list = []
            for sheet_name in sheet_names:
                sheet_obj = xl_file.sheet_by_name(sheet_name)
                for i in range(1, sheet_obj.nrows):
                    data_list.append(sheet_obj.row_values(i))
            suc = 0
            i = 1
            for data in data_list:
                if not data[0]:
                    return JsonResponse({'code': 500, 'msg': '第%d行第1列的主机IP地址不能为空！' % (i+1)}, status=500)
                if IPGroup.objects.filter(IP=data[0], IP_status=1).count() == 0:
                    return JsonResponse({'code': 500, 'msg': '第%d行第1列的主机IP地址不存在或者无效！' % (i + 1)}, status=500)
                if not data[1]:
                    return JsonResponse({'code': 500, 'msg': '第%d行第2列的协议不能为空！' % (i+1)}, status=500)
                if data[1] not in dict(FortServer.server_protocols).values():
                    return JsonResponse({'code': 500, 'msg': '第%d行第2列的协议无效！' % (i+1)}, status=500)
                if data[2] and not re.compile('\d+').match(str(data[2])):
                    return JsonResponse({'code': 500, 'msg': '第%d行第3列的连接端口必须为非空格/回车或数字！' % (i+1)}, status=500)
                if data[1]!='vnc' and not data[3]:
                    return JsonResponse({'code': 500, 'msg': '第%d行第4列的用户名称不能为空！'%(i+1)}, status=500)
                if data[1]!='vnc' and not data[4]:
                    return JsonResponse({'code': 500, 'msg': '第%d行第5列的用户密码不能为空！' % (i+1)}, status=500)
                # if data[1] in ['ssh', 'telnet'] and not data[5]:
                #     return JsonResponse({'code': 500, 'msg': '第%d行第6列的字符编码不能为空！' % (i+1)}, status=500)
                if data[5] and data[5] not in dict(FortServerUser.fort_code_).values():
                    return JsonResponse({'code': 500, 'msg': '第%d行第6列的字符编码无效！' % (i+1)}, status=500)
                if data[1]=='vnc' and not data[6]:
                    return JsonResponse({'code': 500, 'msg': '第%d行第7列的VNC端口不能为空！' % (i+1)}, status=500)
                i += 1
            for data in data_list:
                try:
                    ip = IPGroup.objects.get(IP=data[0], IP_status=1)
                    server_protocol = [v[0] for v in FortServer.server_protocols if v[1]==data[1]][0]
                    if FortServer.objects.filter(server=ip, server_protocol=server_protocol).count() == 0:
                        fortserver = {
                            'server': ip,
                            'server_protocol': server_protocol,
                            'server_status':1
                        }
                        if data[2]:
                            fortserver['port'] = int(data[2])
                        else:
                            if server_protocol == 'telnet':
                                fortserver['port']=23
                            elif server_protocol == 'rdp':
                                fortserver['port'] = 3389
                            else:
                                fortserver['port'] = 22
                        fortserver_obj = FortServer.objects.create(**fortserver)
                    else:
                        fortserver_obj = FortServer.objects.get(server=ip, server_protocol=server_protocol)
                    if FortServerUser.objects.filter(fort_server=fortserver_obj, fort_username=data[3], fort_vnc_port=data[6]).count()>0:
                        continue
                    fortserveruser = {
                        'fort_server': fortserver_obj,
                        'fort_username': data[3] if data[3] else '',
                        'fort_password': CryptPwd().encrypt_pwd(data[4]) if data[4] else '',
                        'fort_user_status': 1,
                        'fort_vnc_port': data[6] if data[6] and data[1]=='vnc' else '',
                        'fort_user_memo': data[7],
                        # 'fort_code': [v[0] for v in FortServerUser.fort_code_ if v[1]==data[5]][0] if data[1] in ['ssh', 'telnet'] else 1
                        'fort_code': [v[0] for v in FortServerUser.fort_code_ if v[1]==data[5]][0] if data[5] else 1
                    }
                    FortServerUser.objects.create(**fortserveruser)
                    suc += 1
                except:
                    pass
            return JsonResponse({'code': 200, 'msg': '成功导入'+str(suc)+'条记录！'})
        except Exception as e:
            return JsonResponse({'code': 500, 'msg': '导入失败！，{}'.format(e)}, status=500)


@permission_required('fort.ssh_fortserver', raise_exception=True)
def terminal_upload_file(request, fort_user_id):
    fort_user_obj = FortServerUser.objects.get(id=fort_user_id)
    fort_username = fort_user_obj.fort_username
    server_ip = fort_user_obj.fort_server.server.IP
    if request.method == 'POST':
        try:
            upload_files = request.FILES.getlist('upload_file')
            port = request.POST.get('upload_port') if request.POST.get('upload_port') else fort_user_obj.fort_server.port
            upload_path = request.POST.get('upload_path').strip() if request.POST.get('upload_path_type') == 'custom' else None
            upload_file_path = os.path.join(settings.MEDIA_ROOT, 'fort_files', request.user.username, 'upload',
                                            server_ip)
            upload_path_check = True if request.POST.get('upload_path_check') == '1' else False
            n = 0
            for upload_file in upload_files:
                sftp = SFTP(server_ip, port, fort_username,
                            CryptPwd().decrypt_pwd(fort_user_obj.fort_password))
                e = sftp.upload_file(upload_file, upload_file_path, upload_path, upload_path_check)
                if e:
                    errs = upload_file.name + '上传失败，失败原因为' + str(e)
                    raise Exception(errs)
                else:
                    n += 1
            return JsonResponse({'code': 200, 'msg': '全部上传成功！文件放在/home/{}路径下'.format(fort_username)})
        except Exception as e:
            return JsonResponse({'code': 500, 'msg': '成功上传{}个文件！{}'.format(n,e)}, status=500)

@permission_required('fort.ssh_fortserver', raise_exception=True)
def terminal_download_file(request, fort_user_id):
    fort_user_obj = FortServerUser.objects.get(id=fort_user_id)
    fort_username = fort_user_obj.fort_username
    server_ip = fort_user_obj.fort_server.server.IP

    if request.method == 'POST':
        download_file = request.POST.get('download_file')
        if download_file:
            port = request.POST.get('download_ftp_number') if request.POST.get('download_ftp_number') else fort_user_obj.fort_server.port
            download_file_path = os.path.join(settings.MEDIA_ROOT, 'fort_files', request.user.username, 'download',
                                              server_ip)
            try:
                sftp = SFTP(server_ip, port, fort_username,
                        CryptPwd().decrypt_pwd(fort_user_obj.fort_password))
                return sftp.download_file(download_file, download_file_path)
            except Exception as e:
                return JsonResponse({'code': 500, 'msg': '下载失败！原因：{}'.format(e)}, status=500)
